package com.tencent.txcc.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.tencent.txcc.CourseService;
import com.tencent.txcc.domain.Course;
import com.tencent.txcc.domain.CourseMarket;
import com.tencent.txcc.domain.KillActivity;
import com.tencent.txcc.domain.KillCourse;
import com.tencent.txcc.dto.KillCourseOrderDto;
import com.tencent.txcc.dto.KillPreOrderParamDto;
import com.tencent.txcc.enums.GlobleExceptionCode;
import com.tencent.txcc.mapper.KillCourseMapper;
import com.tencent.txcc.result.JSONResult;
import com.tencent.txcc.service.IKillActivityService;
import com.tencent.txcc.service.IKillCourseService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.tencent.txcc.util.AssertUtil;
import com.tencent.txcc.util.CodeGenerateUtils;
import com.tencent.txcc.vo.CourseInfoVo;
import com.tencent.txcc.vo.CourseOrderVo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author director
 * @since 2022-10-20
 */
@Slf4j
@Service
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {

    @Autowired
    private IKillCourseService killCourseService;

    @Autowired
    private CourseService courseService;

    @Autowired
    private IKillActivityService killActivityService;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public void addKillCourse(KillCourse killCourse) {
        // 2.根据课程Id查询课程是否存在，是否上架
        // 必须有查出来的课程
        JSONResult jsonResult = courseService.get(killCourse.getCourseId());
        AssertUtil.isTrue(jsonResult.isSuccess(), GlobleExceptionCode.ERROR);

        Object data = jsonResult.getData();
        AssertUtil.isNotNull(data, GlobleExceptionCode.ERROR);

        Course course = JSON.parseObject(JSON.toJSONString(data), Course.class);
        boolean boo = course.getStatus() == Course.STATUS_ON_LINE;
        AssertUtil.isTrue(boo, GlobleExceptionCode.ERROR);

        // 3.判断活动是否是下架状态
        Long activityId = killCourse.getActivityId();
        KillActivity killActivity = killActivityService.selectById(activityId);
        Integer publishStatus = killActivity.getPublishStatus();
        boolean bool = KillActivity.WAIT_PUBLISH == publishStatus;
        AssertUtil.isTrue(bool, GlobleExceptionCode.ERROR);

        // 4.判断此课程是否已经加入到秒杀活动中了
        KillCourse killCourseTemp = selectKillActivityByCourseIdAndActivityId(killCourse.getCourseId(), killCourse.getActivityId());
        AssertUtil.isNull(killCourseTemp, GlobleExceptionCode.ERROR);

        initKillCourse(killCourse, killActivity);
        killCourseService.insert(killCourse);
    }

    @Override
    public List<KillCourse> onlineAll() {
        List<KillCourse> killCourses = new ArrayList<>();
        Set<Object> keys = redisTemplate.keys("activity:*");
        keys.forEach(key ->{
            // values：根据大key直接拿到对应的小value
            List values = redisTemplate.opsForHash().values(key);
            killCourses.addAll(values);
        });
        return killCourses;
    }

    @Override
    public KillCourse onlineOne(Long killActivityId, Long killCourseId) {
        return (KillCourse) redisTemplate.opsForHash().get("activity:" + killActivityId, killCourseId.toString());
    }

    @Override
    public String kill(KillCourseOrderDto killCourseOrderDto) {
        Date date = new Date();
        Long userId = 42L;
        // 1.参数校验使用JSR303
        // 2.业务校验：课程是否上架
        // 2.1.看Redis中是否有此商品
        Object killCourseTmp = redisTemplate.opsForHash().get("activity:" + killCourseOrderDto.getKillActivityId(), killCourseOrderDto.getKillCourseId().toString());
        AssertUtil.isNotNull(killCourseTmp, GlobleExceptionCode.ERROR);
        // 2.2.活动状态是否是正在秒杀中
        KillCourse killCourse = JSON.parseObject(JSON.toJSONString(killCourseTmp), KillCourse.class);
        AssertUtil.isTrue(killCourse.getKilling(), GlobleExceptionCode.ERROR);
        // 2.3.每个用户一个活动的一个商品只能下一次
//        Object preOrderNoTmp = redisTemplate.opsForValue().get(userId + ":" + killCourseOrderDto.getKillActivityId() + ":" + killCourseOrderDto.getKillCourseId());
//        AssertUtil.isNull(preOrderNoTmp, GlobalExceptionCode.ERROR);

        // 3.扣减信号量
        RSemaphore semaphore = redissonClient.getSemaphore(killCourseOrderDto.getKillActivityId() + ":" + killCourseOrderDto.getKillCourseId());
        boolean isAcquire = semaphore.tryAcquire(1);
        // 3.1.秒杀失败
        AssertUtil.isTrue(isAcquire, GlobleExceptionCode.ERROR);

        // 4.创建预创订单
        KillPreOrderParamDto killPreOrderParamDto = new KillPreOrderParamDto();

        killPreOrderParamDto.setKillCourseId(killCourse.getId());
        killPreOrderParamDto.setCourseId(killCourse.getCourseId());
        killPreOrderParamDto.setCourseName(killCourse.getCourseName());
        killPreOrderParamDto.setCoursePic(killCourse.getCoursePic());
        killPreOrderParamDto.setKillActivityId(killCourse.getActivityId());
        String orderNo = CodeGenerateUtils.generateOrderSn(userId);
        killPreOrderParamDto.setOrderNo(orderNo);
        killPreOrderParamDto.setTotalCount(1);
        killPreOrderParamDto.setTotalAmount(killCourse.getKillPrice());
        killPreOrderParamDto.setUserId(userId);

        // 5.预创订单存入Redis
        redisTemplate.opsForValue().set(orderNo, killPreOrderParamDto);

        // 6.当前用户秒杀成功存一个唯一标识
//        redisTemplate.opsForValue().set(userId + ":" + killCourseOrderDto.getKillActivityId() + ":" + killCourseOrderDto.getKillCourseId(), orderNo);

        log.info("秒杀耗时：{}", System.currentTimeMillis() - date.getTime());

        return orderNo;
    }

    @Override
    public CourseInfoVo killOnline(String orderNo) {
        // 1.从Redis中查询预创订单数据
        Object o = redisTemplate.opsForValue().get(orderNo);
        AssertUtil.isNotNull(o, GlobleExceptionCode.ERROR);
        KillPreOrderParamDto killPreOrderParamDto = JSON.parseObject(JSON.toJSONString(o), KillPreOrderParamDto.class);


        CourseInfoVo courseInfoVo = new CourseInfoVo();
        List<CourseOrderVo> courseOrderVos = new ArrayList<>();

        // 3.遍历所有课程封装返回值
        CourseOrderVo courseOrderVo = new CourseOrderVo();

        Course course = new Course();
        course.setName(killPreOrderParamDto.getCourseName());
        course.setPic(killPreOrderParamDto.getCoursePic());
        CourseMarket courseMarket = new CourseMarket();
        courseMarket.setPrice(killPreOrderParamDto.getTotalAmount());

        courseOrderVo.setCourse(course);
        courseOrderVo.setCourseMarket(courseMarket);

        courseInfoVo.setTotalAmount(killPreOrderParamDto.getTotalAmount());

        courseOrderVos.add(courseOrderVo);
        courseInfoVo.setCourseInfos(courseOrderVos);

        return courseInfoVo;
    }

    private void initKillCourse(KillCourse killCourse, KillActivity killActivity) {
        killCourse.setCreateTime(new Date());
        killCourse.setStartTime(killActivity.getStartTime());
        killCourse.setEndTime(killActivity.getEndTime());
        killCourse.setKillLimit(1);
        killCourse.setTimeStr(killActivity.getTimeStr());
        killCourse.setPublishStatus(killActivity.getPublishStatus());
    }

    private KillCourse selectKillActivityByCourseIdAndActivityId(Long activityId, Long courseId) {

        Wrapper<KillCourse> wrapper = new EntityWrapper<>();
        wrapper.eq("activity_id", activityId);
        wrapper.eq("course_id", courseId);
        return killCourseService.selectOne(wrapper);
    }
}
